#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <thread>
#include <cstring>

#include "log.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"
#include "Daemon.hpp"

extern Log lg;

const int backlog = 10; // 但是一般不要设置的太大

enum // 枚举常量
{
    UsageError = 1,
    SocketError,
    BindError,
    ListenError,
};


class serve
{
public:
    serve(uint16_t port = 8888, const std::string &ip = "0.0.0.0")
        : _listensock(-1), _port(port), _ip(ip)
    {}
    ~serve()
    {if(_listensock >= 0) close(_listensock);}

    void init()
    {
        // 1、构建sockfd
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if(_listensock < 0)
        {
            lg(Fatal, "socket fail, errno:%d, reason:%s", errno, strerror(errno));
            exit(SocketError);
        }
        lg(Info, "socket create success!");

        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt)); 
        // 防止偶发性的服务器无法进行立即重启(tcp协议的时候再说)

        //2、填写struct sockaddr_in 字段
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(_port); //转化
        //local.sin_addr.s_addr = INADDR_ANY;
        inet_aton(_ip.c_str(), &local.sin_addr);
        socklen_t len = sizeof(struct sockaddr_in);
        //3、bind
        int ret_bind = bind(_listensock, (const struct sockaddr*)&local, len);
        if(ret_bind < 0)
        {
            lg(Fatal, "bind fail, error:%d, reason:%s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "bind success!");

        // Tcp是面向连接的，服务器一般是比较“被动的”，服务器一直处于一种，一直在等待连接到来的状态
        if (listen(_listensock, backlog) < 0)
        {
            lg(Fatal, "listen error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(ListenError);
        }

        lg(Info, "listen socket success, listensock_: %d", _listensock);
        //_listensock就好比招呼客人进店的，他们不提供服务，但是要链接服务和客人
    }

    void run()
    {
        //在启动之前，独立守护进程
        Daemon();

        //第一次启动的时候
        ThreadPool<Task>::GetInstance()->Start(); //启动单例模式下的线程池
        while(true)
        {
            //必须重新bind
            struct sockaddr_in client;
            socklen_t len = sizeof(struct sockaddr_in);
            int sockfd = accept(_listensock, (struct sockaddr*)&client, &len); //accept会阻塞，等待client和serve链接
            if(sockfd < 0)
            {
                lg(Warning, "accept fail... errno:%d, reason:%s", errno, strerror(errno));
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            // 2. 根据新连接来进行通信
            lg(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, clientip, clientport);    
            
            // version-1：单进程版本。问题：只允许一个客户端进行访问，只有客户端退出，才允许另外一个客户端进入。
            // service_test(sockfd, clientip, clientport);
            // close(sockfd); //当service_test服务结束之后，关闭文件描述符

            // version-2：多进程版本。问题：创建进程的代价太大 适合微型软件
            // pid_t pid = fork();
            // if(pid == 0)
            // {
            //     //子进程
            //     close(_listensock); //子进程关闭listenfd。因为父子进程是共享的
            //     pid_t id = fork();
            //     if(id > 0) exit(0); //子进程退出，留下孙子进程变成孤儿进程执行后面代码
            //     service_test(sockfd, clientip, clientport);
            //     close(sockfd); //孙子进程关闭当前sockfd
            //     exit(0); //孙子进程退出
            // }
            // close(sockfd); //父进程关闭sockfd，避免多余的fd打开
            // pid_t ret_wait = waitpid(pid, nullptr, 0); //父进程等待子进程
            // //父进程继续循环执行

            // version-3：多线程版本。但是服务器处理客户端的信息时只需响应一次即可 适合小型
            // 很显然：我们都已经创建好了socket...等等资源，我们才创建线程
            // std::thread t([this, sockfd, &clientip, &clientport](){
            //     this->service_test(sockfd, clientip, clientport);
            // });
            // t.detach(); //线程跟主线程分离，主线程不再等待，而是直接进入下一次的创建sockfd

            // version-4：线程池版本
            ThreadPool<Task>::GetInstance()->Push(Task(sockfd, clientip, clientport));
            //处理数据

        }
    }

    
    // void service_test(int sockfd, const std::string &clientip, const uint16_t &clientport)
    // //使用 telnet ip port 本机连接！！！
    // {
    //     // 测试代码
    //     char buffer[4096];
    //     while (true)
    //     {
    //         ssize_t n = read(sockfd, buffer, sizeof(buffer));
    //         if (n > 0)
    //         {
    //             buffer[n] = 0;
    //             std::cout << "client say# " << buffer << std::endl;
    //             std::string echo_string = "tcpserver echo# ";
    //             echo_string += buffer;

    //             write(sockfd, echo_string.c_str(), echo_string.size());
    //         }
    //         else if (n == 0)
    //         {
    //             lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
    //             break;
    //         }
    //         else
    //         {
    //             lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
    //             break;
    //         }
    //     }
    //     close(sockfd);
    // }

private:
    int _listensock;      // 监听fd
    const uint16_t _port;  // 端口号
    const std::string _ip; // 服务器ip
};